﻿/*
 * Program.cs
 * 
 * 智能门禁系统的主要逻辑实现
 * 各项功能的具体实现参见其它类
 * 
 * By developer_ken
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using OpenCvSharp;
using static SmGateDemo.FaceRecognition;

namespace SmGateDemo
{
    class Program
    {
        static event Action<Prediction> PersonFoundEvent;
        static Camera Cam;
        static Voicer Voice;
        static TempMeter Temp;
        static GateController Gate;
        public static bool DEBUG = false;
        static void Main(string[] args)
        {
            DEBUG |= Environment.GetEnvironmentVariable("SMG_DEBUG") != null && (Environment.GetEnvironmentVariable("SMG_DEBUG").Length > 0);
#if DEBUG
            DEBUG = true;
#endif
            PersonFoundEvent += Program_PersonFoundEvent;//注册事件，当人脸识别发现人脸时触发

            #region 人脸识别体温监测初始化和消息循环
            Gate = new GateController();
            Gate.Init();//初始化门
            Voice = new Voicer();
            Voice.Init();
            DateTime lastgc = DateTime.Now;//记录当前时间


            /*选取一个COM口和温度计通讯*/
            string[] ports = TempMeter.GetPorts();//获得系统上所有COM
            Console.Write("Potrs:");
            foreach (string ss in ports)
            {
                Console.Write(ss + ";");
            }
            string port = ports[0];//默认选择第一个COM口
#if DEBUG
            //port = "COM3";
#endif
            if (args.Length > 0 && (new List<string>(ports).Contains(args[0])))
            {
                port = args[0];//如果手动指定了COM且有效,则使用手动指定的
            }
            Console.WriteLine("\nUsing Port:" + port);
            Temp = new TempMeter(port);//使用选择的端口实例化温度计
            Temp.Init();//启动温度计

            Console.Write("\n初始化已知人脸...");
            /*初始化人脸识别，导入已知人脸*/
            FaceRecognition facerecog = new FaceRecognition(double.MaxValue/3);//实例化人脸识别
            Dictionary<Mat, int> knownfaces = new Dictionary<Mat, int>();//已知的人脸数据,Mat到名字的字典
            Dictionary<int, string> namedic = new Dictionary<int, string>();//Label到名字的字典
            namedic.Add(-1, "未知");

            int nid = 0;
            var files = Directory.GetFiles("./known");//列出文件
            foreach (string file in files)//该循环遍历每一个文件，建立上述的两个字典
            {
                int uuid = nid;
                string pathh = Path.GetFullPath(file);
                string name = Path.GetFileNameWithoutExtension(pathh).Split('_')[0];
                Log("[DEBUG]name=" + name + "; picture=" + pathh);
                if (!namedic.ContainsValue(name))
                {
                    Log("[DEBUG]name=" + name + "; NEW PERSON;");
                    namedic.Add(uuid, name);
                }
                else
                {
                    foreach (KeyValuePair<int, string> item in namedic)
                    {
                        if (item.Value == name) uuid = item.Key;
                    }
                }
                Log("[DEBUG]name=" + name + "; label=" + uuid + ";");
                knownfaces.Add(Mat.FromImageData(File.ReadAllBytes(pathh)), uuid);
                if (uuid == nid) nid++;
            }
            facerecog.RegisterFaces(knownfaces);//将已知人脸丢给识别引擎训练

            Console.WriteLine("\nDone.");

            Cam = new Camera();//实例化相机(默认设备0)
            Cam.Init();//初始化相机
            Gate.Close();//关门


            /* 消息循环 */
            while (true)
            {
                var frame = Cam.GetLatestFrame();//拍一张照
                if (frame == null) continue;//没找到人脸
                double quality = Camera.QualityRate(frame);//计算清晰度
                var id = facerecog.GetFaceId(frame);//识别图片中的一个人
                if (id.Confidence > 0)//确实认出了人脸
                {
                    //frame.SaveImage("tmp.png");
                    Console.Write("Q=" + quality + ";\tT=" + Temp.Temperature.Calibrated + ";  ");
                    Console.Write(namedic[id.Lable] + "(" + id.Confidence + "); ");
                    PersonFoundEvent?.Invoke(id);
                    Console.WriteLine();
                }
                if ((DateTime.Now - lastgc).TotalSeconds >= 10)//定时垃圾回收，防止内存爆掉
                {
                    Log("[DEBUG]GC.Collect();");
                    GC.Collect();
                    lastgc = DateTime.Now;
                }
            }
            #endregion
            //程序阻塞！！
        }

        public static void Log(string str)
        {
            if (DEBUG)
            {
                Console.WriteLine(str);
            }
        }

        private static void Program_PersonFoundEvent(Prediction obj)
        {
            float temp = Temp.Temperature.Calibrated;//获取传感器经校准的温度
            Log("[DEBUG]Temp.Raw=" + Temp.Temperature.Raw + "; Temp.Cali=" + Temp.Temperature.Calibrated + "; Temp.Elasp=" + (DateTime.Now - Temp.Temperature.TimeOfMeasurement).TotalSeconds);
            int ppi = 3;
            string envr = Environment.GetEnvironmentVariable("SMG_ADD");
            if (envr != null)
            {
                ppi = int.Parse(envr);
            }
            temp += ppi;//加上偏移
            Log("[DEBUG]Temp.Fin=" + temp);
            if (obj.Lable >= 0 && temp <= 400 && temp >= 34)
            {
                //人认识且符合体温条件
                //开门，10秒后关门
                Log("[DEBUG]Open the door.");
                Gate.Open();
                Voice.PlayVoice();
                Thread.Sleep(10 * 1000);
                Log("[DEBUG]Close the door.");
                Gate.Close();
            }
            else
            {
                Log("[DEBUG]Non pass");
                Gate.Close();
            }
        }
    }
}
